package com.cn.gmall.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cn.gmall.client.CartFeignClient;
import com.cn.gmall.common.constant.RedisConst;
import com.cn.gmall.common.mqconst.MqConst;
import com.cn.gmall.common.service.RabbitService;
import com.cn.gmall.common.util.HttpClientUtil;
import com.cn.gmall.model.cart.CartInfo;
import com.cn.gmall.model.enums.OrderStatus;
import com.cn.gmall.model.enums.ProcessStatus;
import com.cn.gmall.model.order.OrderDetail;
import com.cn.gmall.model.order.OrderInfo;
import com.cn.gmall.model.order.OrderTradeVo;
import com.cn.gmall.model.user.UserAddress;
import com.cn.gmall.order.mapper.OrderDetailMapper;
import com.cn.gmall.order.mapper.OrderInfoMapper;
import com.cn.gmall.order.service.OrderService;
import com.cn.gmall.user.client.UserFeignClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DeadlockLoserDataAccessException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.security.Key;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    OrderDetailMapper orderDetailMapper;
    @Resource
    OrderInfoMapper orderInfoMapper;
    @Resource
    CartFeignClient cartFeignClient;
    @Resource
    UserFeignClient userFeignClient;
    @Resource
    RedisTemplate redisTemplate;
    @Autowired
    private RabbitService rabbitService;


    //    获取nacos配置属性
    @Value("${ware.url}")
    public String wareUrl;

    /***
     * 拆单
     *
     *  拆单规则
     *  [{"wareId":"1","skuIds":["21","22","24"]},{"wareId":"2","skuIds":["27"]}]
     * @param orderId
     * @param wareSkuMap
     * @return
     */
    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
//    定义集合收集子订单
        ArrayList<OrderInfo> subOrderList = new ArrayList<>();
//        获取总订单(原订单)
        OrderInfo originOrderInfo = getOrderById(Long.parseLong(orderId));
//      拆单的规则
        List<Map> mapList = JSONObject.parseArray(wareSkuMap, Map.class);
//------------------- 遍历 拆分原订单--------------------
        for (Map map : mapList) {
//            获取仓库id
            String wareId = (String) map.get("wareId");
// [{"wareId":"1","skuIds":["21","22","24"]},{"wareId":"2","skuIds":["27"]}]
//            创建子订单
            OrderInfo subOrderInfo = new OrderInfo();
//            拷贝数据(将原订单的数据 -> 子订单)
            BeanUtils.copyProperties(originOrderInfo, subOrderInfo);
//        置空子订单的id
            subOrderInfo.setId(null);
//            仓库字段
            subOrderInfo.setWareId(wareId);
            //获取父订单所有商品明细
            List<OrderDetail> orderDetailList = originOrderInfo.getOrderDetailList();

//     定义子订单明细集合
            List<OrderDetail> subOrderDetailList = new ArrayList<>();

            //获取拆分的skuid的规则"skuIds":["21","22","24"]
            List<String> skuIds = (List<String>) map.get("skuIds");

//-------------获取当前子订单的商品-->为拆分的子订单找对应的子订单明细----------------
            if (!CollectionUtils.isEmpty(orderDetailList)) {
//   遍历原订单的商品明细集合
                for (OrderDetail orderDetail : orderDetailList) {
//           库存管理系统 返回的分仓库 存储sku的Id集合数组
                    for (String skuId : skuIds) {
//                    每一个商品原订单详情 都与仓库返回的skuId比较 将满足的订单加到子订单明细中
                        if (orderDetail.getSkuId().toString().equals(skuId)) {
                            //收集符合的子订单明细
                            subOrderDetailList.add(orderDetail);

                        }
                    }
                }
            }

            //设置子订单明细
            subOrderInfo.setOrderDetailList(subOrderDetailList);
            //总金额更新--订单明细
            subOrderInfo.sumTotalAmount();

//------定义字符串拼接对象(存放子订单的skuName 长度过100则截取前100的内容)用作订单描述------------------
            StringBuilder builder = new StringBuilder();
            //订单概述--订单明细
            for (OrderDetail orderDetail : subOrderDetailList) {

                builder.append(orderDetail.getSkuName()).append(",");
            }
            //判断
            if (builder.toString().length() > 100) {

                subOrderInfo.setTradeBody(builder.toString().substring(0, 100));
            }
            subOrderInfo.setTradeBody(builder.toString());

//----------------------保存子订单---------------------
            orderInfoMapper.insert(subOrderInfo);
//--------------------保存子订单明细----------------------
            for (OrderDetail orderDetail : subOrderDetailList) {
// ----------------设置子订单id-->到订单详情------------------------
                orderDetail.setOrderId(subOrderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            }
//---------------------------修改(父)原订单状态-->SPLIT(已拆分)-----------
            updateOrderStatus(originOrderInfo.getId(), ProcessStatus.SPLIT);
//----------------------------将拆分完的子订单-->存放在集合里--------------
            subOrderList.add(subOrderInfo);
        }

        return subOrderList;
    }

    /**
     * 扣减库存+ 发消息(mq)
     *
     * @param orderId
     */
    @Override
    public void sendOrderSatus(Long orderId) {
//更新订单状态(OTIFIED_WARE--->已通知仓储)
        updateOrderStatus(orderId, ProcessStatus.NOTIFIED_WARE);
//        整合数据
        String wareJson = initWareOrder(orderId);
//        发送修改库存的消息
        rabbitService.sendMessage(
                MqConst.EXCHANGE_DIRECT_WARE_STOCK,
                MqConst.ROUTING_WARE_STOCK,
                wareJson
        );

    }

    /***
     * 根据id查询订单，转换成json数据
     * @param orderId
     * @return
     */
    private String initWareOrder(Long orderId) {

        OrderInfo orderById = getOrderById(orderId);
//        转换成订单的对象类型  map
//        Map<String,Object> resultMap=this.initWareOrder(orderInfo);
        Map<String, Object> resultMap = this.initWareOrder(orderById);
//        转换json字符串
        return JSONObject.toJSONString(resultMap);
    }

    /***
     * * 转化orderInfo为map对象，符合库存系统的json数据结构
     * @param orderInfo
     * @return
     */
    @Override
    public Map<String, Object> initWareOrder(OrderInfo orderInfo) {
// 新建返回的转化对象
        HashMap<String, Object> resultMap = new HashMap<>();
//    设置数据
        resultMap.put("orderId", orderInfo.getId());
        resultMap.put("consignee", orderInfo.getConsignee());
        resultMap.put("consigneeTel", orderInfo.getConsigneeTel());
        resultMap.put("orderComment", orderInfo.getOrderComment());
        resultMap.put("orderBody", orderInfo.getTradeBody());
        resultMap.put("deliveryAddress", orderInfo.getDeliveryAddress());
//        resultMap.put("paymentWay","ONLINE".equals(orderInfo.getPaymentWay())?2:1);
        resultMap.put("paymentWay", "2");   //只实现了支付宝用2集合
        resultMap.put("wareId", orderInfo.getWareId()); //拆单需要
//    订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
//     遍历订单明细 封装成map对象

        List<HashMap<String, Object>> mapList = orderDetailList.stream().map(orderDetail -> {
//            定义封装对象
            HashMap<String, Object> detail = new HashMap<>();
            detail.put("skuId", orderDetail.getSkuId());
            detail.put("skuNum", orderDetail.getSkuNum());
            detail.put("skuName", orderDetail.getSkuName());
//orderDetail(对象) ->detail(map)
            return detail;
        }).collect(Collectors.toList());

//      将mapList 添加到resultMap
        resultMap.put("details", mapList);


        return resultMap;

    }

    /***
     *根据 out_trade_no查找订单
     * @param out_trade_no
     * @return
     */
    @Override
    public OrderInfo findOrderByOutTradeNo(String out_trade_no) {

        return orderInfoMapper.selectOne(
                new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOutTradeNo, out_trade_no));
    }

    /**
     * 修改订单状态
     *
     * @param orderId
     * @param processStatus 一个枚举类
     */
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
// 修改对象
        OrderInfo orderInfo = new OrderInfo();
//        orderInfo.setId(orderId);
        orderInfo.setProcessStatus(processStatus.name());
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
//   设置条件对象
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderInfo::getId,orderId);

        orderInfoMapper.update(orderInfo,queryWrapper);


    }

    /***
     * 取消订单
     * @param orderId
     */
    @Override
    public void execExpiredOrder(Long orderId, String flag) {

//        修改订单状态
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
//发送消息 关闭支付宝
        if ("2".equals(flag)) {

            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,
                    MqConst.ROUTING_PAYMENT_CLOSE,
                    orderId);
        }
    }

    /**
     * 找orderInfo
     *
     * @param orderId
     * @return
     */
    @Override
    public OrderInfo getOrderById(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

//    查询订单详情   避免向扣库存空指针异常
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>()
                .eq(OrderDetail::getOrderId, orderId));
        if (!CollectionUtils.isEmpty(orderDetails)) {
            //设置订单明细列表到订单中

            orderInfo.setOrderDetailList(orderDetails);
        }

        return orderInfo;
    }

    /***
     * 查看我的订单
     * @param userId
     * @param orderInfoPage
     * @return
     */
    @Override
    public IPage<OrderInfo> findOrdersByUserId(String userId, Page<OrderInfo> orderInfoPage) {
//自定义sql查询订单  不用处理page对象 由page拦截器处理 在sql结尾会加上 limit ?
        IPage<OrderInfo> orderInfoIPage = orderInfoMapper.selectOrderByUserId(userId, orderInfoPage);
        for (OrderInfo record : orderInfoIPage.getRecords()) {

            String orderStatus = record.getOrderStatus(); //UNPAID
            record.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderStatus));
        }

        return orderInfoIPage;
    }

    /**
     * 去结算
     *
     * @param userId
     * @return
     */
    @Override
    public Map<String, Object> toTrade(String userId) {
        HashMap<String, Object> resultMap = new HashMap<>();

//        获取用户地址
        List<UserAddress> userAddressList = userFeignClient.findUserAddressListByUserId(Long.parseLong(userId));
        resultMap.put("userAddressList", userAddressList);

//        获取送货清单
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(userId);
//                      将购物项 转换成订单项
        if (!CollectionUtils.isEmpty(cartCheckedList)) {
//            购物总数量
            AtomicReference<Integer> count = new AtomicReference<>(0);

            List<OrderDetail> orderDetailList = cartCheckedList.stream().map(cartInfo -> {
//                创建订单项
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setSkuId(cartInfo.getSkuId());
                orderDetail.setSkuName(cartInfo.getSkuName());
                orderDetail.setImgUrl(cartInfo.getImgUrl());
                orderDetail.setSkuNum(cartInfo.getSkuNum());
                orderDetail.setOrderPrice(cartInfo.getSkuPrice());

//                记录总数量(在lambda表达式中要变换类型)
                count.updateAndGet(v -> v + cartInfo.getSkuNum());
                return orderDetail;
            }).collect(Collectors.toList());
            resultMap.put("detailArrayList", orderDetailList);

//      --------     计算总金额-------------   OrderInfo对象里面提供了计算总金额的方法
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderDetailList(orderDetailList);
//            调用计算金额方法
            orderInfo.sumTotalAmount();
            resultMap.put("totalAmount", orderInfo.getTotalAmount());
//    ----------    计算总数量---------------
            resultMap.put("totalNum", count.get());
//   ----------获得流水号 -------------
            resultMap.put("tradeNo", this.getTradeNo(userId));

        }
        return resultMap;
    }

    /***
     * 校验流水号
     * @param userId
     * @param tradeNo
     * @return
     */
    @Override
    public boolean checkTradeCode(String userId, String tradeNo) {
//   存储key user:1:tradeNo
        String tradeKey = this.getTradeKey(userId);
        String tradeCode = (String) redisTemplate.opsForValue().get(tradeKey);
        if (StringUtils.isEmpty(tradeCode)) {
            return false;
        }

// 比较返回两个流水号的是否相等
        return tradeNo.equals(tradeCode);
    }

    /***
     * 校验库存
     * @param skuId
     * @param skuNum
     * @return
     */
    @Override
    public boolean checkStock(Long skuId, Integer skuNum) {
        //定义请求路径
        String url = wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum;
        //结果是0或者1
        String result = HttpClientUtil.doGet(url);
        //判断
        if (StringUtils.isEmpty(result)) {
            return false;
        }
        return result.equals("1");
    }


    /***
     * 提交订单
     * @param orderInfo
     * @return
     */
    @Override
    @Transactional
    public Long submitOrder(OrderInfo orderInfo) {
        //补充orderInfo数据
        //总金额
        orderInfo.sumTotalAmount();
        //订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //订单交易编号--支付宝对接(范围>100 ~ <1000)
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(900) + 100;
        orderInfo.setOutTradeNo(outTradeNo);
        //订单描述
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //定义字符串拼接对象
        StringBuilder builder = new StringBuilder();
        for (OrderDetail orderDetail : orderDetailList) {
            builder.append(orderDetail.getSkuName() + "  ");

        }
        //判断长度
        if (builder.length() >= 50) {

            orderInfo.setTradeBody(builder.toString().substring(0, 50));

        } else {
            orderInfo.setTradeBody(builder.toString());

        }
        //操作时间
        orderInfo.setOperateTime(new Date());
        //失效时间--一天
        Calendar calendar = Calendar.getInstance();
        //加一天
        calendar.add(Calendar.DATE, 1);

        //获取时间对象
        Date time = calendar.getTime();

        orderInfo.setExpireTime(time);
        //进度状态
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        //保存order_info
        orderInfoMapper.insert(orderInfo);


        //保存order_detail
        for (OrderDetail orderDetail : orderDetailList) {
            //关联订单id
            orderDetail.setOrderId(orderInfo.getId());

            orderDetailMapper.insert(orderDetail);
        }

//        发送延时消息
        rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,
                MqConst.ROUTING_ORDER_CANCEL,
                orderInfo.getId(),
                MqConst.DELAY_TIME);

        //返回订单id

        return orderInfo.getId();
    }

    /***
     * 删除流水号
     * @param userId
     */
    @Override
    public void deleteTradeNo(String userId) {
//获取key
        String tradeKey = this.getTradeKey(userId);
        redisTemplate.delete(tradeKey);

    }

    /***
     * 生成流水号
     * @param userId
     * @return
     */
    @Override
    public String getTradeNo(String userId) {
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
//        定义redis存储的key
        String tradeKey = RedisConst.USER_KEY_PREFIX + userId + RedisConst.TRADENO_SUFFIX;
//存储
        redisTemplate.opsForValue().set(tradeKey, tradeNo, RedisConst.SECKILL__TIMEOUT, TimeUnit.SECONDS);
        return tradeNo;
    }

    /***
     *获取redis存取key'
     */
    private String getTradeKey(String userId) {

        //定义存储key user:1:tradeNo
        String tradeKey = RedisConst.USER_KEY_PREFIX + userId + RedisConst.TRADENO_SUFFIX;
        return tradeKey;
    }
}
